//
// Created by 李传智 on 2018/4/12.
//

#include "FFDemux.h"
#include "Logger.h"

extern "C" {
#include <libavformat/avformat.h>
}

FFDemux::FFDemux() {
    // 注册所有的封装器
    av_register_all();
    // 注册所有的解码器
    avcodec_register_all();
    // 初始化网络
    avformat_network_init();
}

// 打开文件，或者流媒体 rtsp, rmtp, http
bool FFDemux::open(const char *url) {
    close();
    mutex.lock();
    LOG_I("open file %s", url);
    AVDictionary* opts = NULL;
    av_dict_set(&opts, "rtsp_transport", "tcp", 0);
    int ret = avformat_open_input(&fmtContext, url, 0, &opts);
    if (ret != 0) {
        char buf[1024] = {0};
        av_strerror(ret, buf, sizeof(buf) - 1);
        LOG_E("FFDemux open %s failed, ret = %s", url, av_err2str(ret));
        mutex.unlock();
        return false;
    }
    LOG_I("FFDemux open %s success", url);

    // 读取文件的信息
    ret = avformat_find_stream_info(fmtContext, 0);
    if (ret != 0) {
        char buf[1024] = {0};
        av_strerror(ret, buf, sizeof(buf) - 1);
        LOG_E("FFDemux avformat_find_stream_info %s failed", url);
        mutex.unlock();
        return false;
    }
    // AV_TIME_BASE 一秒有多少个时间单位
    this->duration = fmtContext->duration / (AV_TIME_BASE / 1000);

    LOG_I("duration = %lld", duration);

    mutex.unlock();

    this->getVideoCodecParameters();

    this->getAudioCodecParameters();

    return true;
}

void FFDemux::close() {
    mutex.lock();
    if (fmtContext) {
        avformat_close_input(&fmtContext);
    }
    mutex.unlock();
}

VideoData FFDemux::read() {
    mutex.lock();
    VideoData data;
    if (!fmtContext) {
        mutex.unlock();
        return data;
    }
    int ret;
    AVPacket *pkt;

    pkt = av_packet_alloc();
    ret = av_read_frame(fmtContext, pkt);
    if (ret != 0) {
        mutex.unlock();
        //LOG_I("已读到文件结尾。");
        av_packet_free(&pkt);
        return data;
    }
    //LOG_I("package size = %d, pts = %lld", pkt->size, pkt->pts);

    data.size = pkt->size;
    if (pkt->stream_index == this->audioStreamIdx || pkt->stream_index == this->videoStreamIdx) {
        // 只处理音视频流
        if (pkt->stream_index == this->audioStreamIdx) {
            data.codecType = AVMEDIA_TYPE_AUDIO;
            data.isAudio = true;
        } else if (pkt->stream_index == this->videoStreamIdx) {
            data.codecType = AVMEDIA_TYPE_VIDEO;
            data.isAudio = false;
        }
        data.data = (unsigned char *) pkt;
    } else {
        mutex.unlock();
        av_packet_free(&pkt);
        return data;
    }

    // 转换成 毫秒
    pkt->pts = (int64_t) (pkt->pts * 1000 *
                          av_q2d(fmtContext->streams[pkt->stream_index]->time_base));
    pkt->dts = (int64_t) (pkt->dts * 1000 *
                          av_q2d(fmtContext->streams[pkt->stream_index]->time_base));
    data.pts = pkt->pts;

    mutex.unlock();
    //LOG_I("pkt->pts = %lld, pkt->dts = %lld", pkt->pts, pkt->dts);

    return data;
}

CodecParameter FFDemux::getVideoCodecParameters() {
    mutex.lock();
    CodecParameter parameter = CodecParameter();
    if (!fmtContext) {
        LOG_E("video getVideoCodecParameters failed, fmtContext is NULL");
        mutex.unlock();
        return parameter;
    }
    // 视频流的索引
    int index;
    // 查找视频流的索引
    index = av_find_best_stream(fmtContext, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);
    if (index < 0) {
        LOG_E("video av_find_best_stream failed");
        mutex.unlock();
        return parameter;
    }
    videoStreamIdx = index;
    parameter.parameters = fmtContext->streams[index]->codecpar;
    mutex.unlock();
    return parameter;
}

CodecParameter FFDemux::getAudioCodecParameters() {
    mutex.lock();
    CodecParameter parameter = CodecParameter();
    if (!fmtContext) {
        LOG_E("video getAudioCodecParameters failed, fmtContext is NULL");
        mutex.unlock();
        return parameter;
    }
    // 音频流的索引
    int index;
    // 查找音频流的索引
    index = av_find_best_stream(fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0);
    if (index < 0) {
        LOG_E("audio av_find_best_stream failed");
        mutex.unlock();
        return parameter;
    }
    audioStreamIdx = index;
    parameter.parameters = fmtContext->streams[index]->codecpar;
    parameter.channels = fmtContext->streams[index]->codecpar->channels;
    parameter.sampleRate = fmtContext->streams[index]->codecpar->sample_rate;
    parameter.format = fmtContext->streams[index]->codecpar->format;
    LOG_E("channels: %d, sampleRate: %d, format: %d", parameter.channels, parameter.sampleRate, parameter.format);
    mutex.unlock();
    return parameter;
}

bool FFDemux::seek(double position) {
    if (position < 0 || position > 1) {
        LOG_E("position %lf must between 0 and 1", position);
        return false;
    }
    bool ret = false;

    mutex.lock();

    if (!fmtContext) {
        LOG_E("seek failed, fmtContext is NULL");
        mutex.unlock();
        return ret;
    }

    // 清理读取的缓冲
    avformat_flush(fmtContext);

    int64_t duration = fmtContext->streams[videoStreamIdx]->duration;
    if (duration <= 0) {
        LOG_E("seek failed, video stream duration does not specify");
        mutex.unlock();
        return false;
    }
    int64_t seekPts = (int64_t) (duration * position);
    // 往后跳转到关键帧
    ret = av_seek_frame(fmtContext,
                        videoStreamIdx,
                        seekPts,
                        AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD) >= 0;

    mutex.unlock();

    return ret;
}